# 基础篇 3:小程序架构及其实现机制
# 小程序 VS HTML5
小程序并不是 HTML5 应用,而是更偏向于传统的 CS 架构,它是基于数据驱动的模式,一切皆组件(视图组件)。下面是小程序与普通 Web App 的对比。
- 普通 HTML5 都是执行在浏览器的宿主环境,浏览器提供
window、document等 BOM 对象,但小程序没有window、document,它更像是一个类似 Node.js 的宿主环境;因此在小程序内不能使用document.querySelector这类 DOM 选择器,也不支持XMLHttpRequest、location、localStorage等这些浏览器提供的 API,只能使用小程序自己实现的 API - 小程序并非是直接通过 URL 访问的,而是通过信道服务进行通信和会话管理,所以它不支持 Cookie 存储,同时访问资源使用
wx.request则不存在跨域的问题 - 小程序在 JavaScript 的模块化上支持 CommonJS,通过 require 加载,跟 Node.js 类似
- 小程序的页面样式完全继承了 CSS 的语法,但是在选择器上面会少一些,布局支持 flex 布局
- 小程序的整体框架采用面向状态编程方式,状态管理从 API 来看采用类似 Redux 的设计方式;单向数据绑定方式,当 View 在 Action 操作后,只能通过 Action 的业务处理来更新 View
页面组件模块上,WXML 提供了一整套的「自定义 UI 组件标签」,有些组件实际是 HTML5 实现的,有些组件为了解决权限、性能和适配等问题实际是 Native 实现的(如 map、input、canvas、video)。
笔者在 Android 手机上通过「设置 -> 开发人员选项 -> 显示布局界限」选择显示布局界限之后,打开小程序的页面会看到 Native 的边框,如果是 Native 组件则会展现出来,下面是今日头条小程序的截图。

而使用 X5 内核的 inspect 版本(X5 内核 debug 功能在 12 节真机调试技巧部分有详细介绍),打开 Chrome 的远程调试,看到当前页面如下图所示。

从图中看到,今日头条的小程序顶部导航区域是 Native 组件(会显示布局界限),而底部没有边框,在 Chrome 内发现实际为 webview 的页面实现。
# 小程序架构解密

小程序架构如上图所示,分为视图层和逻辑层,视图层是在 WebView 内渲染,逻辑层则有 JavaScriptCore 来渲染;其中视图层可以多个(考虑到整体性能,最多可以 5 个),逻辑层则全局只有一个(实际通过开启 X5 内核另起一个 JavascriptCore 线程)。
视图层是 WebView,逻辑层为 JavaScriptCore,证据如下:使用 Android 手机,开启 X5 内核 debug 之后,在 Chrome inspect 中可以看到下图所示的内容。

在小程序内,视图层负责页面渲染,逻辑层负责逻辑处理、全局状态管理、请求和接口调用。逻辑层在小程序中称为 APP Service,视图层称为 View。
逻辑层和视图层通过微信的 JSBridge 来实现通信的,逻辑层数据变化通过 JSBridge 通知视图层,触发视图层更新;当视图层触发事件,则继续通过 JSBridge 将事件通知到逻辑层做处理,如此交互进行。
JSBridge 在三个环境(开发者工具、iOS 和 Android)中实现机制不同,在调用 Native 能力时主要使用 invokeHandler:
- 开发者工具:通过
window.postMessage来封装 - iOS:通过 WKWebview 的
window.webkit.messageHandlers.invokeHandler.postMessage - Android:通过
WeixinJSCore.invokeHandler
在消息分发的时候,则使用 publishHandler:
- 开发者工具:通过
addEventListener('message')来监听消息,然后处理分发 - iOS:使用 WKWebview 的
window.webkit.messageHandlers.publishHandler.postMessage - Android:通过
WeixinJSCore.publishHandler
其中,Android 的 WeixinJSCore 是 X5 内核暴露出来的对象,其作为 window 对象的一个属性,提供一些供 JavaScript 调用的能力。
这部分可以在开发者工具或者 X5 内核 debug 模式下,找到 WAService.js(代码笔者放到了这个 Gist 上,方便大家查看) 看到:
WeixinJSBridge 提供的方法有 invoke、
